package observermall

import (
	"fmt"
	"sync"
)

// Subject 主题接口
type Subject interface {
	// Register 注册观察者
	Register(observer Observer)
	// Deregister 注销观察者
	Deregister(observer Observer)
	// Notify 通知观察者
	Notify(eventType string)
}

// Product 具体主题 - 商品
type Product struct {
	ID          string
	Name        string              // 商品名称
	Price       float64             // 价格
	Stock       int                 // 库存
	Subscribers map[string]Observer // 订阅者id -> Observer
	mutex       sync.Mutex
}

func NewProduct(id, name string, price float64, stock int) *Product {
	return &Product{
		ID:          id,
		Name:        name,
		Price:       price,
		Stock:       stock,
		Subscribers: map[string]Observer{},
	}
}

// Register 注册观察者
func (p *Product) Register(observer Observer) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	p.Subscribers[observer.GetID()] = observer
	fmt.Printf("用户 %s 订阅了商品 %s\n", observer.GetID(), p.Name)
}

// Deregister 注销观察者
func (p *Product) Deregister(observer Observer) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	delete(p.Subscribers, observer.GetID())
	fmt.Printf("用户 %s 取消订阅了商品 %s\n", observer.GetID(), p.Name)
}

// Notify 通知观察者
func (p *Product) Notify(eventType string, msg string) {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	if len(p.Subscribers) == 0 {
		fmt.Printf("商品 %s 没有订阅者，不需要通知\n", p.Name)
		return
	}

	fmt.Printf("【通知】商品 %s 发生事件: %s\n", p.Name, eventType)
	var wg sync.WaitGroup

	for _, observer := range p.Subscribers {
		wg.Add(1)
		go func(observer Observer) {
			defer wg.Done()
			observer.Update(p, eventType, msg)
		}(observer)
	}
	wg.Wait()
}

func (p *Product) SetPrice(newPrice float64) {
	if newPrice == p.Price {
		return
	}
	
	oldPrice := p.Price
	p.Price = newPrice
	
	if newPrice < oldPrice {
		p.Notify(EventTypePriceDrop, fmt.Sprintf("(%.2f → %.2f)", oldPrice, newPrice))
	} else {
		p.Notify(EventTypePriceRise, fmt.Sprintf("(%.2f → %.2f)", oldPrice, newPrice))
	}
}

func (p *Product) SetStock(newStock int) {
	if newStock == p.Stock {
		return
	}
	
	oldStock := p.Stock
	p.Stock = newStock
	
	if newStock == 0 {
		p.Notify(EventTypeOutOfStock, "")
	} else if oldStock == 0 && newStock > 0 {
		p.Notify(EventTypeReplenish, "")
	} else if newStock > oldStock {
		p.Notify(EventTypeStockIncrease, fmt.Sprintf("(%d → %d)", oldStock, newStock))
	} else {
		p.Notify(EventTypeStockDecrease, fmt.Sprintf("(%d → %d)", oldStock, newStock))
	}
}

func (p *Product) AddNewVariant(variantName string, price float64) {
	p.Notify(EventTypeNewVariant, fmt.Sprintf("%s (¥%.2f)", variantName, price))
}
